/***************************************************************************
 *
 * This file is provided under a dual BSD/GPLv2 license.  When using or
 *   redistributing this file, you may do so under either license.
 * 
 *   GPL LICENSE SUMMARY
 * 
 *   Copyright(c) 2007-2022 Intel Corporation. All rights reserved.
 * 
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of version 2 of the GNU General Public License as
 *   published by the Free Software Foundation.
 * 
 *   This program is distributed in the hope that it will be useful, but
 *   WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   General Public License for more details.
 * 
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *   The full GNU General Public License is included in this distribution
 *   in the file called LICENSE.GPL.
 * 
 *   Contact Information:
 *   Intel Corporation
 * 
 *   BSD LICENSE
 * 
 *   Copyright(c) 2007-2022 Intel Corporation. All rights reserved.
 *   All rights reserved.
 * 
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 * 
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 * 
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * 
 *
 ***************************************************************************/

/**
 ***************************************************************************
 * @file lac_mem.h
 *
 * @defgroup LacMem     Memory
 *
 * @ingroup LacCommon
 *
 * Memory re-sizing functions and memory accessor macros.
 *
 ***************************************************************************/

#ifndef LAC_MEM_H
#define LAC_MEM_H

/***************************************************************************
 * Include header files
 ***************************************************************************/
#include "cpa.h"
#include "Osal.h"
#include "qae_mem.h"
#include "lac_common.h"

/**
 *******************************************************************************
 * @ingroup LacMem
 *      These macros are used to Endian swap variables from IA to QAT.
 *
 * @param[out] x    The variable to be swapped.
 *
 * @retval none
 ******************************************************************************/
#if (LAC_BYTE_ORDER == __LITTLE_ENDIAN)
#define LAC_MEM_WR_64(x) OSAL_HOST_TO_NW_64(x)
#define LAC_MEM_WR_32(x) OSAL_HOST_TO_NW_32(x)
#define LAC_MEM_WR_16(x) OSAL_HOST_TO_NW_16(x)
#define LAC_MEM_RD_64(x) OSAL_NW_TO_HOST_64(x)
#define LAC_MEM_RD_32(x) OSAL_NW_TO_HOST_32(x)
#define LAC_MEM_RD_16(x) OSAL_NW_TO_HOST_16(x)
#else
#define LAC_MEM_WR_64(x) (x)
#define LAC_MEM_WR_32(x) (x)
#define LAC_MEM_WR_16(x) (x)
#define LAC_MEM_RD_64(x) (x)
#define LAC_MEM_RD_32(x) (x)
#define LAC_MEM_RD_16(x) (x)
#endif

#define LAC_MEM_SYSTEM_PAGE_SIZE OSAL_PAGE_SIZE
/**< @ingroup LacMem
 * Size of the OS PAGE size as defined by OSAL */

/*
*******************************************************************************
* Shared Memory Macros (memory accessible by Acceleration Engines, e.g. QAT)
*******************************************************************************
*/

/**
 *******************************************************************************
 * @ingroup LacMem
 *      This macro can be used to write to a variable that will be read by the
 * QAT. The macro will automatically detect the size of the target variable and
 * will select the correct method for performing the write. The data is cast to
 * the type of the field that it will be written to.
 * This macro swaps data if required.
 *
 * @param[out] var    The variable to be written. Can be a field of a struct.
 *
 * @param[in] data    The value to be written.  Will be cast to the size of the
 *                    target.
 *
 * @retval none
 ******************************************************************************/
#define LAC_MEM_SHARED_WRITE_SWAP(var, data)                                   \
    do                                                                         \
    {                                                                          \
        switch (sizeof(var))                                                   \
        {                                                                      \
            case 1:                                                            \
                (var) = (Cpa8U)(data);                                         \
                break;                                                         \
            case 2:                                                            \
                (var) = (Cpa16U)(data);                                        \
                (var) = LAC_MEM_WR_16(((Cpa16U)var));                          \
                break;                                                         \
            case 4:                                                            \
                (var) = (Cpa32U)(data);                                        \
                (var) = LAC_MEM_WR_32(((Cpa32U)var));                          \
                break;                                                         \
            case 8:                                                            \
                (var) = (Cpa64U)(data);                                        \
                (var) = (Cpa32U)LAC_MEM_WR_64(((Cpa64U)var));                  \
                break;                                                         \
            default:                                                           \
                break;                                                         \
        }                                                                      \
    } while (0)

/**
 *******************************************************************************
 * @ingroup LacMem
 *      This macro can be used to read a variable that was written by the QAT.
 * The macro will automatically detect the size of the data to be read and will
 * select the correct method for performing the read. The value read from the
 * variable is cast to the size of the data type it will be stored in.
 * This macro swaps data if required.
 *
 * @param[in] var     The variable to be read. Can be a field of a struct.
 *
 * @param[out] data   The variable to hold the result of the read. Data read
 *                    will be cast to the size of this variable
 *
 * @retval none
 ******************************************************************************/
#define LAC_MEM_SHARED_READ_SWAP(var, data)                                    \
    do                                                                         \
    {                                                                          \
        switch (sizeof(var))                                                   \
        {                                                                      \
            case 1:                                                            \
                (data) = (var);                                                \
                break;                                                         \
            case 2:                                                            \
                (data) = LAC_MEM_RD_16(((Cpa16U)var));                         \
                break;                                                         \
            case 4:                                                            \
                (data) = LAC_MEM_RD_32(((Cpa32U)var));                         \
                break;                                                         \
            case 8:                                                            \
                (data) = LAC_MEM_RD_64(((Cpa64U)var));                         \
                break;                                                         \
            default:                                                           \
                break;                                                         \
        }                                                                      \
    } while (0)

/**
 *******************************************************************************
 * @ingroup LacMem
 *      This macro can be used to write a pointer to a QAT request. The fields
 *      for pointers in the QAT request and response messages are always 64 bits
 *
 * @param[out] var    The variable to be written to. Can be a field of a struct.
 *
 * @param[in] data    The value to be written.  Will be cast to size of target
 *                    variable
 *
 * @retval none
 ******************************************************************************/
/* cast pointer to scalar of same size of the native pointer */
#define LAC_MEM_SHARED_WRITE_FROM_PTR(var, data)                               \
    ((var) = (Cpa64U)(LAC_ARCH_UINT)(data))

/* Note: any changes to this macro implementation should also be made to the
 * similar LAC_MEM_CAST_PTR_TO_UINT64 macro
 */

/**
 *******************************************************************************
 * @ingroup LacMem
 *      This macro can be used to read a pointer from a QAT response. The fields
 *      for pointers in the QAT request and response messages are always 64 bits
 *
 * @param[in] var     The variable to be read. Can be a field of a struct.
 *
 * @param[out] data   The variable to hold the result of the read. Data read
 *                    will be cast to the size of this variable
 *
 * @retval none
 ******************************************************************************/
/* Cast back to native pointer */
#define LAC_MEM_SHARED_READ_TO_PTR(var, data)                                  \
    ((data) = (void *)(LAC_ARCH_UINT)(var))

/**
 *******************************************************************************
 * @ingroup LacMem
 *      This macro safely casts a pointer to a Cpa64U type.
 *
 * @param[in] pPtr   The pointer to be cast.
 *
 * @retval pointer cast to Cpa64U
 ******************************************************************************/
#define LAC_MEM_CAST_PTR_TO_UINT64(pPtr) ((Cpa64U)(pPtr))

/**
 *******************************************************************************
 * @ingroup LacMem
 *      This macro converts from a virtual address to a physical address for
 *      internally allocated memory.
 *
 * @param[in] pGenService Pointer to sal_service_t structure.
 * @param[in] pVirtAddr   The address to be converted.
 *
 * @retval The converted physical address
 ******************************************************************************/
#ifdef USER_SPACE
#define LAC_OS_VIRT_TO_PHYS_INTERNAL(pGenService, pVirtAddr)                   \
    ((SalMem_virt2PhysInternal(pVirtAddr, (void *)pGenService)))
#else
#define LAC_OS_VIRT_TO_PHYS_INTERNAL(pGenService, pVirtAddr)                   \
    (OSAL_MMU_VIRT_TO_PHYS(pVirtAddr))
#endif

/**
 *******************************************************************************
 * @ingroup LacMem
 *      This macro should be called on all externally allocated memory it calls
 *      SalMem_virt2PhysExternal function which allows a user
 *      to set the virt2phys function used by an instance.
 *      Defaults to osal virt to phys for kernel.
 *
 * @param[in] genService  Generic sal_service_t structure.
 * @param[in] pVirtAddr   The address to be converted.
 *
 * @retval The converted physical address
 ******************************************************************************/
#define LAC_OS_VIRT_TO_PHYS_EXTERNAL(genService, pVirtAddr)                    \
    ((SalMem_virt2PhysExternal(pVirtAddr, &(genService))))

/**
 *******************************************************************************
 * @ingroup LacMem
 *      This macro can be used to write an address variable that will be read by
 * the QAT.  The macro will perform the necessary virt2phys address translation
 * This macro is to be used on memory allocated externally by the user. It calls
 * the user supplied virt2phys address translation.
 *
 * @param[in] pService The pointer to the service
 * @param[out] var     The address variable to write. Can be a field of a struct
 * @param[in] pPtr     The pointer variable to containing the address to be
 *                     written
 *
 * @retval none
 ******************************************************************************/
#define LAC_MEM_SHARED_WRITE_VIRT_TO_PHYS_PTR_EXTERNAL(pService, var, pPtr)    \
    do                                                                         \
    {                                                                          \
        Cpa64U physAddr = 0;                                                   \
        physAddr = LAC_MEM_CAST_PTR_TO_UINT64(                                 \
            LAC_OS_VIRT_TO_PHYS_EXTERNAL(pService, pPtr));                     \
        var = physAddr;                                                        \
    } while (0)

/*
*******************************************************************************
* OS Memory Macros
*******************************************************************************
*/

/**
 *******************************************************************************
 * @ingroup LacMem
 *      This function and associated macro allocates the memory for the given
 *      size and stores the address of the memory allocated in the pointer.
 *
 * @param[out] ppMemAddr    address of pointer where address will be stored
 * @param[in] sizeBytes     the size of the memory to be allocated.
 *
 * @retval CPA_STATUS_RESOURCE  Macro failed to allocate Memory
 * @retval CPA_STATUS_SUCCESS   Macro executed successfully
 *
 ******************************************************************************/
static __inline CpaStatus LacMem_OsMemAlloc(void **ppMemAddr, Cpa32U sizeBytes)
{
    *ppMemAddr = osalMemAlloc(sizeBytes);
    if (NULL == *ppMemAddr)
    {
        return CPA_STATUS_RESOURCE;
    }

    return CPA_STATUS_SUCCESS;
}

/**
 *******************************************************************************
 * @ingroup LacMem
 *      This function and associated macro allocates the contiguous
 *       memory for the given
 *      size and stores the address of the memory allocated in the pointer.
 *
 * @param[out] ppMemAddr     address of pointer where address will be stored
 * @param[in] sizeBytes      the size of the memory to be allocated.
 * @param[in] alignmentBytes the alignment
 * @param[in] node           node to allocate from
 *
 * @retval CPA_STATUS_RESOURCE  Macro failed to allocate Memory
 * @retval CPA_STATUS_SUCCESS   Macro executed successfully
 *
 ******************************************************************************/
static __inline CpaStatus LacMem_OsContigAlignMemAlloc(void **ppMemAddr,
                                                       Cpa32U sizeBytes,
                                                       Cpa32U alignmentBytes,
                                                       Cpa32U node)
{
#if defined(ICP_PARAM_CHECK)
    if ((alignmentBytes & (alignmentBytes - 1)) != 0) /* if is not power of 2 */
    {
        *ppMemAddr = NULL;
        LAC_INVALID_PARAM_LOG("alignmentBytes MUST be the power of 2;\r\n");
        return CPA_STATUS_INVALID_PARAM;
    }
#endif

#ifdef KERNEL_SPACE
    *ppMemAddr = osalMemAllocContiguousNUMA(sizeBytes, node, alignmentBytes);
#else
    *ppMemAddr = qaeMemAllocNUMA(sizeBytes, node, alignmentBytes);
#endif

    if (NULL == *ppMemAddr)
    {
        return CPA_STATUS_RESOURCE;
    }

    return CPA_STATUS_SUCCESS;
}

/**
 *******************************************************************************
 * @ingroup LacMem
 *      Macro from the LacMem_OsMemAlloc function
 *
 ******************************************************************************/
#define LAC_OS_MALLOC(ppMemAddr, sizeBytes)                                    \
    LacMem_OsMemAlloc((void *)(ppMemAddr), (sizeBytes))

/**
 *******************************************************************************
 * @ingroup LacMem
 *      Macro from the LacMem_OsContigAlignMemAlloc function
 *
 ******************************************************************************/
#define LAC_OS_CAMALLOC(ppMemAddr, sizeBytes, alignmentBytes, node)            \
    LacMem_OsContigAlignMemAlloc(                                              \
        (void *)ppMemAddr, sizeBytes, alignmentBytes, node)

/**
 *******************************************************************************
 * @ingroup LacMem
 *      Macro for declaration static const unsigned int constant. One provides
 *   the compilation time computation with the highest bit set in the
 *   sizeof(TYPE) value. The constant is being put by the linker by default in
 *   .rodata section
 *
 *   E.g. Statement LAC_DECLARE_HIGHEST_BIT_OF(lac_mem_blk_t)
 *   results in following entry:
 *     static const unsigned int highest_bit_of_lac_mem_blk_t = 3
 *
 *   CAUTION!
 *      Macro is prepared only for type names NOT-containing ANY
 *  special characters. Types as amongst others:
 *  - void *
 *  - unsigned long
 *  - unsigned int
 *  are strictly forbidden and will result in compilation error.
 *  Use typedef to provide one-word type name for MACRO's usage.
 ******************************************************************************/
#define LAC_DECLARE_HIGHEST_BIT_OF(TYPE)                                       \
  static const unsigned int highest_bit_of_##TYPE =                            \
    (sizeof(TYPE) & 0x80000000 ? 31 :                                          \
    (sizeof(TYPE) & 0x40000000 ? 30 :                                          \
    (sizeof(TYPE) & 0x20000000 ? 29 :                                          \
    (sizeof(TYPE) & 0x10000000 ? 28 :                                          \
    (sizeof(TYPE) & 0x08000000 ? 27 :                                          \
    (sizeof(TYPE) & 0x04000000 ? 26 :                                          \
    (sizeof(TYPE) & 0x02000000 ? 25 :                                          \
    (sizeof(TYPE) & 0x01000000 ? 24 :                                          \
    (sizeof(TYPE) & 0x00800000 ? 23 :                                          \
    (sizeof(TYPE) & 0x00400000 ? 22 :                                          \
    (sizeof(TYPE) & 0x00200000 ? 21 :                                          \
    (sizeof(TYPE) & 0x00100000 ? 20 :                                          \
    (sizeof(TYPE) & 0x00080000 ? 19 :                                          \
    (sizeof(TYPE) & 0x00040000 ? 18 :                                          \
    (sizeof(TYPE) & 0x00020000 ? 17 :                                          \
    (sizeof(TYPE) & 0x00010000 ? 16 :                                          \
    (sizeof(TYPE) & 0x00008000 ? 15 :                                          \
    (sizeof(TYPE) & 0x00004000 ? 14 :                                          \
    (sizeof(TYPE) & 0x00002000 ? 13 :                                          \
    (sizeof(TYPE) & 0x00001000 ? 12 :                                          \
    (sizeof(TYPE) & 0x00000800 ? 11 :                                          \
    (sizeof(TYPE) & 0x00000400 ? 10 :                                          \
    (sizeof(TYPE) & 0x00000200 ?  9 :                                          \
    (sizeof(TYPE) & 0x00000100 ?  8 :                                          \
    (sizeof(TYPE) & 0x00000080 ?  7 :                                          \
    (sizeof(TYPE) & 0x00000040 ?  6 :                                          \
    (sizeof(TYPE) & 0x00000020 ?  5 :                                          \
    (sizeof(TYPE) & 0x00000010 ?  4 :                                          \
    (sizeof(TYPE) & 0x00000008 ?  3 :                                          \
    (sizeof(TYPE) & 0x00000004 ?  2 :                                          \
    (sizeof(TYPE) & 0x00000002 ?  1 :                                          \
    (sizeof(TYPE) & 0x00000001 ?  0 : 32) ))))))))))))))))/*16*/))))))))))))))) /* 31 */

/**
 *******************************************************************************
 * @ingroup LacMem
 *      This function and associated macro frees the memory at the given address
 *      and resets the pointer to NULL
 *
 * @param[out] ppMemAddr    address of pointer where mem address is stored.
 *                          If pointer is NULL, the function will exit silently
 *
 * @retval void
 *
 ******************************************************************************/
static __inline void LacMem_OsMemFree(void **ppMemAddr)
{
    if (NULL != *ppMemAddr)
    {
        osalMemFree(*ppMemAddr);
        *ppMemAddr = NULL;
    }
}

/**
 *******************************************************************************
 * @ingroup LacMem
 *      This function and associated macro frees the contiguous memory at the
 *      given address and resets the pointer to NULL
 *
 * @param[out] ppMemAddr    address of pointer where mem address is stored.
 *                          If pointer is NULL, the function will exit silently
 *
 * @retval void
 *
 ******************************************************************************/
static __inline void LacMem_OsContigAlignMemFree(void **ppMemAddr)
{
    if (NULL != *ppMemAddr)
    {
#ifdef KERNEL_SPACE
        osalMemFreeNUMA(*ppMemAddr);
#else
        qaeMemFreeNUMA(ppMemAddr);
#endif
        *ppMemAddr = NULL;
    }
}

#define LAC_OS_FREE(pMemAddr) LacMem_OsMemFree((void *)&pMemAddr)

#define LAC_OS_CAFREE(pMemAddr) LacMem_OsContigAlignMemFree((void *)&pMemAddr)

/**
 *******************************************************************************
 * @ingroup LacMem
 *     Copies user data to a working buffer of the correct size (required by
 *     PKE services)
 *
 * @description
 *      This function produces a correctly sized working buffer from the input
 *      user buffer. If the original buffer is too small a new buffer shall
 *      be allocated and memory is copied (left padded with zeros to the
 *      required length).
 *
 *      The returned working buffer is guaranteed to be of the desired size for
 *      QAT.
 *
 *      When this function is called pInternalMem describes the user_buffer and
 *      when the function returns pInternalMem describes the working buffer.
 *      This is because pInternalMem describes the memory that will be sent to
 *      QAT.
 *
 *      The caller must keep the original buffer pointer. The allocated buffer
 *      is freed (as necessary) using icp_LacBufferRestore().
 *
 * @param[in] instanceHandle Handle to crypto instance so pke_resize mem pool
 *                           can be located
 * @param[in] pUserBuffer Pointer on the user buffer
 * @param[in] userLen     length of the user buffer
 * @param[in] workingLen  length of the working (correctly sized) buffer
 * @param[in/out] pInternalMem    pointer to boolean if CPA_TRUE on input then
 *                                user_buffer is internally allocated memory
 *                                if false then it is externally allocated.
 *                                This value gets updated by the function
 *                                if the returned pointer references internally
 *                                allocated memory.
 *
 * @return a pointer to the working (correctly sized) buffer or NULL if the
 *      allocation failed
 *
 * @note the working length cannot be smaller than the user buffer length
 *
 * @warning the working buffer may be the same or different from the original
 * user buffer; the caller should make no assumptions in this regard
 *
 * @see icp_LacBufferRestore()
 *
 ******************************************************************************/
Cpa8U *icp_LacBufferResize(CpaInstanceHandle instanceHandle,
                           Cpa8U *pUserBuffer,
                           Cpa32U userLen,
                           Cpa32U workingLen,
                           CpaBoolean *pInternalMemory);

/**
 *******************************************************************************
 * @ingroup LacMem
 *     Restores a user buffer
 *
 * @description
 *      This function restores a user buffer and releases its
 *      corresponding working buffer. The working buffer, assumed to be
 *      previously obtained using icp_LacBufferResize(), is freed as necessary.
 *
 *      The contents are copied in the process.
 *
 * @note the working length cannot be smaller than the user buffer length
 *
 * @param[out] pUserBuffer     Pointer on the user buffer
 * @param[in] userLen          length of the user buffer
 * @param[in] pWorkingBuffer   Pointer on the working buffer
 * @param[in] workingLen       working buffer length
 * @param[in] copyBuf          if set _TRUE the data in the workingBuffer
 *                             will be copied to the userBuffer before the
 *                             workingBuffer is freed.
 *
 * @return the status of the operation
 *
 * @see icp_LacBufferResize()
 *
 ******************************************************************************/
CpaStatus icp_LacBufferRestore(Cpa8U *pUserBuffer,
                               Cpa32U userLen,
                               Cpa8U *pWorkingBuffer,
                               Cpa32U workingLen,
                               CpaBoolean copyBuf);

/**
 *******************************************************************************
 * @ingroup LacMem
 *    Uses an instance specific user supplied virt2phys function to convert a
 *    virtual address to a physical address.
 *
 * @description
 *    Uses an instance specific user supplied virt2phys function to convert a
 *    virtual address to a physical address. A client of QA API can set the
 *    virt2phys function for an instance by using the
 *    cpaXxSetAddressTranslation() function. If the client does not set the
 *    virt2phys function and the instance is in kernel space then OS specific
 *    virt2phys function will be used. In user space the virt2phys function
 *    MUST be set by the user.
 *
 * @param[in] pVirtAddr         the virtual addr to be converted
 * @param[in] pServiceGen       Pointer on the sal_service_t structure
 *                              so client supplied virt2phys function can be
 *                              called.
 *
 * @return the physical address
 *
 ******************************************************************************/
CpaPhysicalAddr SalMem_virt2PhysExternal(void *pVirtAddr, void *pServiceGen);

/**
*******************************************************************************
* @ingroup LacMem
*    Convert a virtual address to a physical address using internal function.
*
* @description
*    Convert a virtual address to a physical address using internal function.
*
* @param[in] pVirtAddr         the virtual addr to be converted
* @param[in] pServiceGen       Pointer on the sal_service_t structure
*
* @return the physical address
*
******************************************************************************/
CpaPhysicalAddr SalMem_virt2PhysInternal(void *pVirtAddr, void *pServiceGen);

#endif /* LAC_MEM_H */
